/*
  Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
  This file is part of the gloox library. http://camaya.net/gloox

  This software is distributed under a license. The full license
  agreement can be found in the file LICENSE in this distribution.
  This software may not be copied, modified, sold or distributed
  other than expressed in the named license agreement.

  This software is distributed without any warranty.
*/



#include "gloox.h"

#include "connectiontcpbase.h"
//#include "dns.h"
#include "logsink.h"
#include "prep.h"
#include "mutexguard.h"
#include "strutil.h"
#include "IapManager.h"

#include <BADESCA.H>
#include <akngloballistquery.h> 
#ifdef __MINGW32__
# include <winsock.h>
#endif

#if ( !defined( _WIN32 ) && !defined( _WIN32_WCE ) ) || defined( __SYMBIAN32__ )
# include <arpa/inet.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <sys/select.h>
# include <netinet/in.h>
# include <unistd.h>
#elif ( defined( _WIN32 ) || defined( _WIN32_WCE ) ) && !defined( __SYMBIAN32__ )
# include <winsock.h>
typedef int socklen_t;
#endif

#include <ctime>

#include <cstdlib>
#include <string>

namespace gloox
{

  ConnectionTCPBase::ConnectionTCPBase( const LogSink& logInstance,
                                        const std::string& server, int port )
    : ConnectionBase( 0 ),
      m_logInstance( logInstance ), m_buf( 0 ), m_totalBytesIn( 0 ),
      m_totalBytesOut( 0 ), m_bufsize( 1024 ), m_cancel( true )
  {
    init( server, port );
  }

  ConnectionTCPBase::ConnectionTCPBase( ConnectionDataHandler* cdh, const LogSink& logInstance,
                                        const std::string& server, int port )
    : ConnectionBase( cdh ),
      m_logInstance( logInstance ), m_buf( 0 ), m_totalBytesIn( 0 ),
      m_totalBytesOut( 0 ), m_bufsize( 1024 ), m_cancel( true )
  {
    init( server, port );
  }

  void ConnectionTCPBase::init( const std::string& server, int port )
  {
// FIXME check return value?
    prep::idna( server, m_server );
    m_port = port;
    m_buf = (char*)calloc( m_bufsize + 1, sizeof( char ) );
    
    m_socketEngine=CTCPSocketEngine::NewL(*this);
  }

  ConnectionTCPBase::~ConnectionTCPBase()
  {
    cleanup();
    free( m_buf );
    m_buf = 0;
  }
  ConnectionError ConnectionTCPBase::connect()
  {
	  if(m_port==-1)
		  m_port=5222;
	  m_state=StateConnecting;
	  TPtrC8 ptr((const TUint8 *)m_server.c_str(),m_server.length());
	  HBufC* server=StrUtil::ConvertToUnicode(ptr);
	  if(m_socketEngine&&server)
	  	      m_socketEngine->ConnectL(server->Des(),m_port);
	  delete server;
	  return ConnNoError;
  }
  void ConnectionTCPBase::disconnect()
  {
    util::MutexGuard rm( m_recvMutex );
    m_cancel = true;
  }

  ConnectionError ConnectionTCPBase::receive()
  {
	 if(m_socketEngine)
	      m_socketEngine->ReceiveL();
    
    return ConnNoError;
  }

  bool ConnectionTCPBase::send( const std::string& data )
  {
    m_sendMutex.lock();

    if( data.empty() )
    {
      m_sendMutex.unlock();
      return false;
    }

    TPtrC8 ptr((const TUint8 *)data.c_str(),data.length());
    m_socketEngine->SendL(ptr);
    
    m_totalBytesOut += (int)data.length();

    m_sendMutex.unlock();
    
    return true;
  }

  void ConnectionTCPBase::getStatistics( long int &totalIn, long int &totalOut )
  {
    totalIn = m_totalBytesIn;
    totalOut = m_totalBytesOut;
  }

  void ConnectionTCPBase::cleanup()
  {
    if( !m_sendMutex.trylock() )
      return;

    if( !m_recvMutex.trylock() )
    {
      m_sendMutex.unlock();
      return;
    }

    delete m_socketEngine;
    m_socketEngine=NULL;

    m_state = StateDisconnected;
    m_cancel = true;
    m_totalBytesIn = 0;
    m_totalBytesOut = 0;

    m_recvMutex.unlock(),
    m_sendMutex.unlock();
  }

  int ConnectionTCPBase::localPort() const
  {
//    struct sockaddr local;
//    socklen_t len = (socklen_t)sizeof( local );
//    if( getsockname ( m_socket, &local, &len ) < 0 )
//      return -1;
//    else
//      return ntohs( ((struct sockaddr_in *)&local)->sin_port );
  }

  const std::string ConnectionTCPBase::localInterface() const
  {
//    struct sockaddr_in local;
//    socklen_t len = (socklen_t)sizeof( local );
//    if( getsockname ( m_socket, (reinterpret_cast<struct sockaddr*>( &local )), &len ) < 0 )
//      return EmptyString;
//    else
//    {
////       char addr[INET_ADDRSTRLEN];
////       return inet_ntop( AF_INET, &(local.sin_addr), addr, sizeof( addr ) ); //FIXME is this portable?
//      return inet_ntoa( local.sin_addr );
//    }
//  }
	  return EmptyString;
  }
  
  void ConnectionTCPBase::OnConnectL(TInt aError) 
  	{
  		if(aError==KErrNone)
  			{	
  		m_state=StateConnected;
  		m_handler->handleConnect( this );
  			}
  		else
  			{
  		m_state=StateDisconnected;
  		m_handler->handleDisconnect( this, ConnConnectionRefused );
  			}
  	}
  void ConnectionTCPBase::OnSendL(const TDesC8& aMsg, TInt aError)
  	{
  	if(aError==KErrNone)
  		{  		
  		m_handler->handleSendData( this, ConnNoError );  		
  		}
  	else
  		{
  		m_state=StateDisconnected;
  		m_handler->handleDisconnect( this, ConnIoError );
  		}
  	}
  void ConnectionTCPBase::OnReceiveL(const TDesC8& aMsg, TInt aError) 
  	{
  	if(aError==KErrNone)
  		{  		
  		const char* ptr=(const char*)aMsg.Ptr();
  		std::string data;
  		data.append(ptr,aMsg.Length());
  		m_handler->handleReceivedData(this,data);
  		}
  	else
  		{		
  		m_state=StateDisconnected;
  		m_handler->handleDisconnect( this, ConnConnectionRefused );
  		}
  	}
  void ConnectionTCPBase::GetIap(TUint32& aIap)
  	{
	  RArray<TIapInfo> aIapInfoList;
	//	if (iIapEnable && iIapTimes++ <= 2)
		{
		IapManager::GetAllIapList(aIapInfoList);
		CDesCArray* array = new (ELeave) CDesCArrayFlat(aIapInfoList.Count());
		CleanupStack::PushL(array);
		for (TInt i = 0; i < aIapInfoList.Count(); i++)
			{
			array->AppendL(aIapInfoList[i].iIapName);
			}
		// the application framework has not be created yet so we the notifier service to show the list
		CAknGlobalListQuery* query = CAknGlobalListQuery::NewLC();
		_LIT(KIapHeader, "请重新选择接入点");			
		query->SetHeadingL(KIapHeader());
		TRequestStatus status = KRequestPending;
		query->ShowListQueryL(array, status);
		User::WaitForRequest(status);
		TInt ret = status.Int();
		CleanupStack::PopAndDestroy(2);
		if (ret >= 0 && ret < aIapInfoList.Count())
			{
			aIap = aIapInfoList[ret].iId;
			aIapInfoList.Reset();
			return;
			}
		} 
  	}
  
  
  void ConnectionTCPBase::SSLHandShakeComplete(TInt err)
  {
	  CertInfo info;
	  
	  if(m_tlsHandler)
		  {
		  bool ret=false;
		  if(err==0)
			  ret=true;
		  m_tlsHandler->handleHandshakeResult(NULL,ret,info);
		  }
  }

  void ConnectionTCPBase::startTls(TLSHandler* tlsHandler)
	  {
	  m_tlsHandler=tlsHandler;
	  if(this->m_socketEngine)
		  m_socketEngine->StartSSL();
	  }
  void ConnectionTCPBase::stopTls()
	  {	  
	  if(this->m_socketEngine)
	 		  m_socketEngine->StopSSL();
	  m_tlsHandler=NULL;
	  }
}


